#include "LecteurPhraseAvecArbre.h"
#include <stdlib.h>
#include <iostream>
#include <typeinfo>

using namespace std;

////////////////////////////////////////////////////////////////////////////////

vector<string> LecteurPhraseAvecArbre::generationAda() {
    vector<string> decla;
    vector<SymboleValue *> table = ts.getTable();
    vector<SymboleValue *>::iterator k;
    unsigned int i = 0;
    k = table.begin();
    while (i < table.size()) {
        if (k == table.begin()) {
            decla.push_back("begin");
        } else if ((**k).estDefini() == false)
            decla.push_back("Integer " + (**k).getChaine() + ";");
        i++;
        k++;
    }
    decla.push_back("end;");
    return decla;
}

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() {
    this->nbErreur = 0;
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    // try{
    arbre = programme();
    //    }
    //    catch(exception & e){
    //        nbErreur++;
    //    }
    if (getNbErreur() == 0)
        cout << "Syntaxe correcte." << endl;
    else
        cout << getNbErreur() << " erreur(s) trouvee(s)" << endl;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER
    try {
        sauterSymCour("debut");
    }
    catch (exception & e) {
        nbErreur++;        
    }
    Noeud* si = seqInst();
    try {
        sauterSymCour("fin");
        testerSymCour("<FINDEFICHIER>");
        return si;
    }    catch (exception & e) {
        nbErreur++;
        return NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }
    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        si->ajouteInstruction(inst());
        try {
            sauterSymCour(";");
        } catch (exception & e) {
            nbErreur++;
        }
    } while (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "si"
            || ls.getSymCour() == "tantque" || ls.getSymCour() == "repeter" ||
            ls.getSymCour() == "lire" || ls.getSymCour() == "ecrire" || ls.getSymCour() == "pour");
    // tant que le symbole courant est un debut possible d'instruction...
    return si;   
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <inst_condi>
    try {
        if (ls.getSymCour() == "si")
            return instSi();
        else if (ls.getSymCour() == "tantque")
            return instTq();
        else if (ls.getSymCour() == "repeter")
            return instRepeter();
        else if (ls.getSymCour() == "lire")
            return instLire();
        else if (ls.getSymCour() == "ecrire")
            return instEcrire();
        else if (ls.getSymCour() == "pour")
            return instPour();
        else if (ls.getSymCour() == "<VARIABLE>")
            return affectation();                       
        else {
            erreur("<inst>");
            return NULL;
        }
    } catch (exception & e) {
        nbErreur++;       
        return (NULL);
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression>
    Noeud* exp;
    testerSymCour("<VARIABLE>");
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour("=");
    exp = expression();
    return new NoeudAffectation(var, exp);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <expression> ::= <facteur> { <opAdd>||<opMult> <facteur> }

    Symbole operateur;
    Noeud* fact = facteur();
    while (ls.getSymCour() == "+" || ls.getSymCour() == "-" ||
            ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        if (ls.getSymCour() == "+" || ls.getSymCour() == "-")
            operateur = opAdd(); // on stocke le symbole de l'opérateur
        else if (ls.getSymCour() == "*" || ls.getSymCour() == "/")
            operateur = opMult(); // on stocke le symbole de l'opérateur
        Noeud* factDroit = facteur(); // lecture de l'operande droit
        fact = new NoeudOperateurBinaire(operateur, fact, factDroit); // const. du noeud
    }
    return fact;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= <flottant> | <chaine> | <entier>  |  <variable>  |  <opUnaire> <expBool> |  ( <expBool> )

    Noeud* fact = NULL;

    if (ls.getSymCour() == "<FLOTTANT>" || ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>" || ls.getSymCour() == "<CHAINE>") {
        fact = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {       
        ls.suivant();      
        fact = new NoeudOperateurUnaire(facteur());
    } else if (ls.getSymCour() == "(") {
        ls.suivant();
        fact = expression();
        sauterSymCour(")");
    } else
        erreur("<facteur>");
    return fact;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() {
    // <relation> ::= <expression> { <opRel> <relation>}

    Noeud* rela = expression();

    while (ls.getSymCour() == "==" || ls.getSymCour() == "!=" ||
            ls.getSymCour() == "<=" || ls.getSymCour() == ">=" ||
            ls.getSymCour() == "<" || ls.getSymCour() == ">") {
        Symbole operateur = opRel();
        Noeud* relaDroite = expression();
        rela = new NoeudOperateurBinaire(operateur, rela, relaDroite);
    }
    return rela;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBool() {
    // <relation> ::= <relation> { <opEt> | <opOu> <relation>}
    Symbole operateurBool;
    Noeud* rela = relation();
    while (ls.getSymCour() == "et" || ls.getSymCour() == "ou") {
        if (ls.getSymCour() == "et")
            operateurBool = opEt();
        else
            operateurBool = opOu();
        Noeud* relaDroite = relation();
        rela = new NoeudOperateurBinaire(operateurBool, rela, relaDroite);
    }
    return rela;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel()
// <opRel> ::= == | != | < | <= | > | >=
{
    Symbole operateur;
    if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" ||
            ls.getSymCour() == "<" || ls.getSymCour() == "<=" ||
            ls.getSymCour() == ">" || ls.getSymCour() == ">=") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opRel>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire()
// <opUnaire> ::= - | non
{
    Symbole operateur;
    if (ls.getSymCour() == "non" || ls.getSymCour() == "-") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opUnaire>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opEt() {
    // <opEt> ::= et 
    Symbole operateur;
    if (ls.getSymCour() == "et") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opEt>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opOu() {
    // <opOu> ::= ou 
    Symbole operateur;
    if (ls.getSymCour() == "ou") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opOu>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme()
// <terme> ::= <facteur> { <opMult> <facteur> }
{
    Noeud* term = facteur();
    Symbole operateur;
    while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        operateur = opMult();
        Noeud* termDroit = facteur();
        term = new NoeudOperateurBinaire(operateur, term, termDroit);
    }
    return term;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd()
// <opAdd> ::= + | - 
{
    Symbole operateur;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opAdd>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult()
// <opMult> ::= *  | /
{
    Symbole operateur;
    if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opMult>");
    return operateur;
}
//////////////////////////////////////////////////////////////////seqInst();//////////////

Noeud* LecteurPhraseAvecArbre::instRepeter()
// repeter <seqInst> jusqua ( <expBool> )
{
    sauterSymCour("repeter");
    Noeud* seq = seqInst();
    sauterSymCour("jusqua");
    sauterSymCour("(");
    Noeud* cond = expBool();
    sauterSymCour(")");
    NoeudInstRepeter* rep = new NoeudInstRepeter(cond, seq);
    return rep;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi()
// si <expBool> alors <seqInst> { sinonsi  <expBool>  <seqInst> }
//     [ sinon <seqInst> ] finsi
{
    NoeudInstSi* si = new NoeudInstSi;
    Noeud* condition;
    Noeud* seqI;
    sauterSymCour("si");
    condition = expBool();
    sauterSymCour("alors");
    seqI = seqInst();
    si->ajoute(condition, seqI);
    while (ls.getSymCour() == "sinonsi") {
        sauterSymCour("sinonsi");
        condition = expBool();
        sauterSymCour("alors");
        seqI = seqInst();
        si->ajoute(condition, seqI);
    }
    if (ls.getSymCour() == "sinon") {
        ls.suivant();
        seqI = seqInst();
        condition = NULL;
        si->ajoute(condition, seqI);
    }
    sauterSymCour("finsi");
    return si;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instTq()
// tantque ( <expBool> ) <seqInst> fintantque
{
    sauterSymCour("tantque");
    sauterSymCour("(");
    Noeud* cond = expBool();
    sauterSymCour(")");
    sauterSymCour("faire");
    Noeud* seq = seqInst();
    sauterSymCour("fintantque");
    NoeudInstTq* tq = new NoeudInstTq(cond, seq);
    return tq;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instLire()
// <instLire> ::= lire(<variable>)
{
    NoeudInstLire* lire;
    Noeud* vari;
    sauterSymCour("lire");
    sauterSymCour("(");
    testerSymCour("<VARIABLE>");
    vari = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour(")");
    lire = new NoeudInstLire(vari);
    return lire;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instEcrire()
// < instEcrire> ::= ecrire(<expression> | <chaine> )
{

    NoeudInstEcrire* ecrire;
    sauterSymCour("ecrire");
    sauterSymCour("(");
    if (ls.getSymCour() == "<CHAINE>") {
        Noeud* chaine = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
        ecrire = new NoeudInstEcrire(chaine);
    } else {
        Noeud* exp = expression();
        ecrire = new NoeudInstEcrire(exp);
    }
    sauterSymCour(")");
    return ecrire;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instPour()
// <instPour> ::= pour(<affectation>;<expBool>;<affectation>)<seqInst> finpour
{
    sauterSymCour("pour");
    sauterSymCour("(");
    Noeud* affectation1 = affectation();
    sauterSymCour(";");
    Noeud* cond = expBool();
    sauterSymCour(";");
    Noeud* affectation2 = affectation();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    sauterSymCour("finpour");
    NoeudInstPour* pour = new NoeudInstPour(affectation1, affectation2, cond, seq);
    return pour;
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    if (ls.getSymCour() != ch) {
        cout << endl << "-------- Erreur ligne " << ls.getLigne()
                << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;       
        throw ErreurEx();
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    testerSymCour(ch);
    ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
            << ls.getColonne() << endl << "   Attendu : " << mess << endl
            << "   Trouve  : " << ls.getSymCour() << endl << endl;    
    throw ErreurEx();
}


